import os
from ctypes import windll, pointer
from ctypes.wintypes import HWND, RECT
import string
import time

import cv2
import win32gui
import win32ui
import win32con
from PIL import Image
import aircv as ac
import numpy as np
from variable import Variable

PostMessageW = windll.user32.PostMessageW
MapVirtualKeyW = windll.user32.MapVirtualKeyW
VkKeyScanA = windll.user32.VkKeyScanA

WM_KEYDOWN = 0x100
WM_KEYUP = 0x101
VkCode = {
    "back": 0x08,
    "tab": 0x09,
    "return": 0x0D,
    "shift": 0x10,
    "control": 0x11,
    "menu": 0x12,
    "pause": 0x13,
    "capital": 0x14,
    "escape": 0x1B,
    "space": 0x20,
    "end": 0x23,
    "home": 0x24,
    "left": 0x25,
    "up": 0x26,
    "right": 0x27,
    "down": 0x28,
    "print": 0x2A,
    "snapshot": 0x2C,
    "insert": 0x2D,
    "delete": 0x2E,
    "lwin": 0x5B,
    "rwin": 0x5C,
    "numpad0": 0x60,
    "numpad1": 0x61,
    "numpad2": 0x62,
    "numpad3": 0x63,
    "numpad4": 0x64,
    "numpad5": 0x65,
    "numpad6": 0x66,
    "numpad7": 0x67,
    "numpad8": 0x68,
    "numpad9": 0x69,
    "multiply": 0x6A,
    "add": 0x6B,
    "separator": 0x6C,
    "subtract": 0x6D,
    "decimal": 0x6E,
    "divide": 0x6F,
    "f1": 0x70,
    "f2": 0x71,
    "f3": 0x72,
    "f4": 0x73,
    "f5": 0x74,
    "f6": 0x75,
    "f7": 0x76,
    "f8": 0x77,
    "f9": 0x78,
    "f10": 0x79,
    "f11": 0x7A,
    "f12": 0x7B,
    "numlock": 0x90,
    "scroll": 0x91,
    "lshift": 0xA0,
    "rshift": 0xA1,
    "lcontrol": 0xA2,
    "rcontrol": 0xA3,
    "lmenu": 0xA4,
    "rmenu": 0xA5,
}

WM_MOUSEMOVE = 0x0200
WM_LBUTTONDOWN = 0x0201
WM_LBUTTONUP = 0x202
WM_MOUSEWHEEL = 0x020A
WHEEL_DELTA = 120


# 根据按键名获取虚拟按键码
def get_virtual_keycode(key: str):
    if len(key) == 1 and key in string.printable:
        return VkKeyScanA(ord(key)) & 0xFF
    else:
        return VkCode[key]


# 按下指定按键
def key_down(key: str):
    vk_code = get_virtual_keycode(key)
    scan_code = MapVirtualKeyW(vk_code, 0)
    wparam = vk_code
    lparam = (scan_code << 16) | 1
    PostMessageW(Variable.handle, WM_KEYDOWN, wparam, lparam)


# 放开指定按键
def key_up(key: str):
    vk_code = get_virtual_keycode(key)
    scan_code = MapVirtualKeyW(vk_code, 0)
    wparam = vk_code
    lparam = (scan_code << 16) | 0xC0000001
    PostMessageW(Variable.handle, WM_KEYUP, wparam, lparam)


def press(key: str, during=0.2):
    key_down(key)
    time.sleep(during)
    key_up(key)
    time.sleep(during)


def confirm_step(
    image_template, confidence=0.8, w=None, h=None, bl=0, bt=0, imgsrc="", findAll=False
):
    imgsrc = (
        os.path.join(os.path.dirname(__file__), imgsrc)
        if imgsrc
        else os.path.join(os.path.dirname(__file__), "./images/current.png")
    )
    image_template = os.path.join(os.path.dirname(__file__), image_template)
    get_src(Variable.handle, w=w, h=h, bl=bl, bt=bt, path=imgsrc)
    imgsrc = ac.imread(imgsrc)  # 打开查找页，大图
    imgobj = cv2.imdecode(
        np.fromfile(image_template, dtype=np.uint8), -1
    )  # 打开待识别的图片，小图
    if findAll:
        return ac.find_all_template(imgsrc, imgobj, confidence)

    return ac.find_template(imgsrc, imgobj, confidence)


def get_src(handle, w=None, h=None, bl=0, bt=0, path=""):
    left, top, right, bot = win32gui.GetWindowRect(handle)
    width = int(
        (right - left) * Variable.scale if not w else int(w / 2 * Variable.scale)
    )
    height = int((bot - top) * Variable.scale if not h else int(h / 2 * Variable.scale))
    # 返回句柄窗口的设备环境，覆盖整个窗口，包括非客户区，标题栏，菜单，边框
    hWndDC = win32gui.GetWindowDC(handle)
    # 创建设备描述表
    mfcDC = win32ui.CreateDCFromHandle(hWndDC)
    # 创建内存设备描述表
    saveDC = mfcDC.CreateCompatibleDC()
    # 创建位图对象准备保存图片
    saveBitMap = win32ui.CreateBitmap()
    # 为bitmap开辟存储空间
    saveBitMap.CreateCompatibleBitmap(mfcDC, width, height)
    # 将截图保存到saveBitMap中
    saveDC.SelectObject(saveBitMap)
    # 保存bitmap到内存设备描述表
    saveDC.BitBlt(
        (0, 0),
        (width, height),
        mfcDC,
        (int(bl / 2 * Variable.scale), int(bt / 2 * Variable.scale)),
        win32con.SRCCOPY,
    )
    # 获取位图信息
    bmpinfo = saveBitMap.GetInfo()
    bmpstr = saveBitMap.GetBitmapBits(True)
    # 生成图像
    im_PIL = Image.frombuffer(
        "RGB", (bmpinfo["bmWidth"], bmpinfo["bmHeight"]), bmpstr, "raw", "BGRX", 0, 1
    )
    # 内存释放
    win32gui.DeleteObject(saveBitMap.GetHandle())
    saveDC.DeleteDC()
    mfcDC.DeleteDC()
    win32gui.ReleaseDC(handle, hWndDC)
    # PrintWindow成功,保存到文件,显示到屏幕
    im_PIL.save(path)  # 保存


# 移动鼠标到坐标（x, y)
def move_to(handle: HWND, x: int, y: int):
    x = int(x)
    y = int(y)
    wparam = 0x0001
    lparam = y << 16 | x
    PostMessageW(handle, WM_MOUSEMOVE, wparam, lparam)


# 在坐标(x, y)按下鼠标左键
def left_down(x: int, y: int):
    x = int(x)
    y = int(y)
    wparam = 0x001
    lparam = y << 16 | x
    PostMessageW(Variable.handle, WM_LBUTTONDOWN, wparam, lparam)


# 在坐标(x, y)放开鼠标左键
def left_up(x: int, y: int):
    x = int(x)
    y = int(y)
    wparam = 0
    lparam = y << 16 | x
    PostMessageW(Variable.handle, WM_LBUTTONUP, wparam, lparam)


def left_click(x, y, delay=2, confirm_pic="", repeat=3):
    left_down(x, y)
    time.sleep(0.15)
    left_up(x, y)
    if confirm_pic != "":
        xy = confirm_step(confirm_pic)
        timer = 0
        while not xy:
            timer += 1
            if timer == repeat + 1:
                return False
            left_click(x, y, 0.5)
            xy = confirm_step(confirm_pic)
    time.sleep(delay)
    return True


def pic_click(
    src,
    delay=2,
    confidence=0.8,
    confirm="",
    conf_flag=True,
):
    xy = confirm_step(src, confidence=confidence)
    if not xy:
        return False
    left_click(
        xy["result"][0] / Variable.scale, xy["result"][1] / Variable.scale, delay
    )
    if confirm != "":
        if (conf_flag and confirm_step(confirm)) or (
            not conf_flag and not confirm_step(confirm)
        ):
            return True
        else:
            return False
    return True


def click(action):
    if action == "intoGame":
        for i in range(3):
            if not pic_click(
                src="./images/icon.png", confirm="./images/icon.png", conf_flag=False
            ):
                print(f"\r找不到游戏图标,重试第{i + 1}次", end="")
                time.sleep(1)
            else:
                if i != 0:
                    print()
                return True
        print()
    return False


if __name__ == "__main__":
    handle = windll.user32.FindWindowW(None, "逍遥模拟器")
    rect = RECT()
    windll.user32.GetWindowRect(handle, pointer(rect))
    width = rect.right - rect.left
    height = rect.bottom - rect.top
    print(confirm_step("./images/icon.png"))
